/*
 * This file is part of QBDI.
 *
 * Copyright 2017 - 2025 Quarkslab
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
#include "ComparedExecutor_Thumb.h"
#include "QBDI/Config.h"
#include "QBDI/Memory.hpp"
#include "Patch/Utils.h"

const char CPU[] = CPU_CPU;
const std::vector<std::string> MATTRS = {CPU_MATTRS, "thumb2", "v7"};

InMemoryObject
ComparedExecutor_Thumb::compileWithContextSwitch(const char *source) {
  std::ostringstream finalSource;

  finalSource << ".thumb\n"
              << "push.w {lr}\n"
              << "add.w r0, r1, #"
              << offsetof(QBDI::Context, fprState.vreg.d[0]) << "\n"
              << "vldmia	r0!, {d0-d15}\n"
#if QBDI_NUM_FPR == 32
              << "vldmia	r0, {d16-d31}\n"
#endif
              << "ldr.w r0, [r1, #" << offsetof(QBDI::Context, gprState.cpsr)
              << "]\n"
              << "msr APSR_nzcvqg, r0\n"
              // backup SP
              << "str.w sp, [r1, #" << offsetof(QBDI::Context, hostState.sp)
              << "]\n"
              // set SP LR
              << "add.w r0, r1, #" << offsetof(QBDI::Context, gprState.sp)
              << "\n"
              << "ldm.w r0, {r2,lr}\n"
              << "mov.w	sp, r2\n"
              // set backup
              << "push.w {r0-r1}\n"
              // set r0-r12 SP LR
              << "add.w r0, r1, #" << offsetof(QBDI::Context, gprState.r0)
              << "\n"
              << "ldm r0, {r0-r12}\n"
              //<< "bkpt #0\n"
              << ".align 2\n"
              << source << "str.w r0, [sp]\n"
              << "ldr.w r0, [sp, #4]\n"
              // backup r1-r12 SP LR
              << "add.w r0, r0, #" << offsetof(QBDI::Context, gprState.r1)
              << "\n"
              << "stm r0!, {r1-r12}\n"
              << "mov r2, sp\n"
              << "stm r0, {r2,lr}\n"
              << "pop.w {r0-r1}\n"
              // backup r0
              << "str.w r0, [r1, #" << offsetof(QBDI::Context, gprState.r0)
              << "]\n"
              // restore sp
              << "ldr.w sp, [r1, #" << offsetof(QBDI::Context, hostState.sp)
              << "]\n"
              << "mrs r0, APSR\n"
              << "str.w r0, [r1, #" << offsetof(QBDI::Context, gprState.cpsr)
              << "]\n"
              << "add.w r0, r1, #"
              << offsetof(QBDI::Context, fprState.vreg.d[0]) << "\n"
              << "vstmia  r0!, {d0-d15}\n"
#if QBDI_NUM_FPR == 32
              << "vstmia  r0, {d16-d31}\n"
#endif
              << "pop.w {lr}\n"
              << "bx lr\n";

  // printf("%s\n",finalSource.str().c_str());

  return InMemoryObject(finalSource.str().c_str(), CPU, "thumb", MATTRS);
}

QBDI::Context ComparedExecutor_Thumb::jitExec(llvm::ArrayRef<uint8_t> code,
                                              QBDI::Context &inputState,
                                              llvm::sys::MemoryBlock &stack) {
  QBDI::Context outputState;
  QBDI::Context outerState;
  llvm::sys::MemoryBlock ctxBlock;
  llvm::sys::MemoryBlock outerStack;
  std::error_code ec;

  ctxBlock = llvm::sys::Memory::allocateMappedMemory(
      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);
  outerStack = llvm::sys::Memory::allocateMappedMemory(
      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);
  memset((void *)&outerState, 0, sizeof(QBDI::Context));
  // Put the inputState on the stack
  inputState.gprState.sp = (QBDI::rword)stack.base() + stack.allocatedSize();

  memcpy((void *)ctxBlock.base(), (void *)&inputState, sizeof(QBDI::Context));
  // Prepare the outerState to fake the realExec() action
  outerState.gprState.sp =
      (QBDI::rword)outerStack.base() + outerStack.allocatedSize();
  outerState.gprState.lr = (QBDI::rword)0;
  outerState.gprState.r1 = (QBDI::rword)ctxBlock.base();

  vm.setGPRState(&outerState.gprState);
  vm.setFPRState(&outerState.fprState);
  vm.addInstrumentedRange((QBDI::rword)code.data(),
                          (QBDI::rword)code.data() + code.size());
  vm.run(((QBDI::rword)code.data()) | 1, 0);
  vm.removeInstrumentedRange((QBDI::rword)code.data(),
                             (QBDI::rword)code.data() + code.size());

  memcpy((void *)&outputState, (void *)ctxBlock.base(), sizeof(QBDI::Context));

  llvm::sys::Memory::releaseMappedMemory(ctxBlock);
  llvm::sys::Memory::releaseMappedMemory(outerStack);

  return outputState;
}

QBDI::Context ComparedExecutor_Thumb::realExec(llvm::ArrayRef<uint8_t> code,
                                               QBDI::Context &inputState,
                                               llvm::sys::MemoryBlock &stack) {

  QBDI::Context outputState;
  std::error_code ec;
  llvm::sys::MemoryBlock ctxBlock;

  ctxBlock = llvm::sys::Memory::allocateMappedMemory(
      4096, nullptr, PF::MF_READ | PF::MF_WRITE, ec);

  // Put the inputState on the stack
  inputState.gprState.sp = (QBDI::rword)stack.base() + stack.allocatedSize();

  // Assemble the sources
  // Copy the input context
  memcpy(ctxBlock.base(), (void *)&inputState, sizeof(QBDI::Context));
  // Execute
  {
    register uint32_t ctxBlockBase asm("r1") = (uint32_t)ctxBlock.base();
    register uint32_t codeData asm("r2") = (((uint32_t)code.data()) | 1);
    asm volatile inline(
        "push {r0, lr}\n"
        "blx r2\n"
        "pop {r0, lr}\n"
        :
        : "r"(codeData), "r"(ctxBlockBase)
        : "r0", "r3", "r4", "r5", "r6", "r7", "r8", "r9", "r10", "r11", "r12",
          "d0", "d1", "d2", "d3", "d4", "d5", "d6", "d7", "d8", "d9", "d10",
          "d11", "d12", "d13", "d14", "d15", "d16", "d17", "d18", "d19", "d20",
          "d21", "d22", "d23", "d24", "d25", "d26", "d27", "d28", "d29", "d30",
          "d31", "memory");
  }
  // Get the output context
  memcpy((void *)&outputState, ctxBlock.base(), sizeof(QBDI::Context));

  llvm::sys::Memory::releaseMappedMemory(ctxBlock);

  return outputState;
}

void ComparedExecutor_Thumb::initContext(QBDI::Context &ctx) {
  memset(&ctx, 0, sizeof(QBDI::Context));
  ctx.gprState.r0 = get_random();
  ctx.gprState.r1 = get_random();
  ctx.gprState.r2 = get_random();
  ctx.gprState.r3 = get_random();
  ctx.gprState.r4 = get_random();
  ctx.gprState.r5 = get_random();
  ctx.gprState.r6 = get_random();
  ctx.gprState.r7 = get_random();
  ctx.gprState.r8 = get_random();
  ctx.gprState.r9 = get_random();
  ctx.gprState.r10 = get_random();
  ctx.gprState.r11 = get_random();
  ctx.gprState.r12 = get_random();
  ctx.gprState.lr = get_random();
}

const char *tGPRSave_s =
    "    mov r0, #1\n"
    "    mov r1, #2\n"
    "    mov r2, #3\n"
    "    mov r3, #4\n"
    "    mov r4, #5\n"
    "    mov r5, #6\n"
    "    mov r6, #7\n"
    "    mov r7, #8\n"
    "    mov r8, #9\n"
    "    mov r9, #10\n"
    "    mov r10, #11\n"
    "    mov r11, #12\n"
    "    mov r12, #13\n"
    "    mov lr, #14\n";

const char *tGPRShuffle_s =
    "    push {r0-r12,lr}\n"
    "    pop {r3}\n"
    "    pop {r11}\n"
    "    pop {r7}\n"
    "    pop {r2}\n"
    "    pop {r12}\n"
    "    pop {r10}\n"
    "    pop {lr}\n"
    "    pop {r1}\n"
    "    pop {r5}\n"
    "    pop {r6}\n"
    "    pop {r0}\n"
    "    pop {r4}\n"
    "    pop {r9}\n"
    "    pop {r8}\n";

const char *tRelativeAddressing_s =
    "    b start\n"
    ".align 2\n"
    "c1:\n"
    "    .word 0x12345678\n"
    "start:\n"
    "    adr.w r0, c1\n"
    "    ldr r1, [r0]\n"
    "    eor.w r12, r12, r1\n"
    "    ldr r2, c2\n"
    "    eor.w r11, r11, r2\n"
    "    b.w t2\n"
    "c2:\n"
    "    .word 0x76543210\n"
    ".align 2\n"
    "t2:\n"
    // test with 4bytes unaligned instruction
    "    nop\n"
    "    adr r3, #12\n"
    "    ldr r4, [r3]\n"
    "    eor.w r10, r10, r4\n"
    "    ldr.w r5, c3\n"
    "    eor.w r9, r9, r5\n"
    "    b.w end\n"
    "    .word 0x76543210\n"
    "c3:\n"
    "    .word 0x89abcdef\n"
    "end:\n";

const char *tFibonacciRecursion_s =
    "    adr r2, fibo\n"
    "    eor r2, r2, #1\n"
    "    blx r2\n"
    "    b end\n"
    ".align 2\n"
    "fibo:\n"
    "    cmp r0, #2\n"
    "    itt ls\n"
    "    movls r0, #1\n"
    "    bxls lr\n"
    "    push {r0, lr}\n"
    "    sub r0, r0, #1\n"
    "    blx r2\n"
    "    pop {r1}\n"
    "    push {r0}\n"
    "    sub r0, r1, #2\n"
    "    blx r2\n"
    "    pop {r1}\n"
    "    add r0, r0, r1\n"
    "    pop {pc}\n"
    "end:\n";

const char *tBranchCondTest_s =
    "    cmp r0, r1\n"
    "    ble t1\n"
    "    add r0, #1\n"
    "    b t2\n"
    "t1:\n"
    "    add r1, #1\n"
    "t2:\n"
    "    cmp r2, r3\n"
    "    it le\n"
    "    ble t3\n"
    "    add r2, #1\n"
    "    b t4\n"
    "t3:\n"
    "    add r3, #1\n"
    "t4:\n"
    "    cmp r4, r5\n"
    "    ble.w t5\n"
    "    add r4, #1\n"
    "    b.w t6\n"
    "t5:\n"
    "    add r5, #1\n"
    "t6:\n"
    "    cmp r6, r7\n"
    "    it le\n"
    "    ble.w t7\n"
    "    add r6, #1\n"
    "    b.w t8\n"
    "t7:\n"
    "    add r8, #1\n"
    "t8:\n"
    "    mov r7, 1\n"
    "    cbnz r7, t9\n"
    "    mov.w r12, #37\n"
    "t9:\n"
    "    cbz r7, t10\n"
    "    mov.w r11, #42\n"
    "t10:\n"
    "    cbz r7, t11\n"
    "    mov.w r10, #42\n"
    "t11:\n"
    "    cbnz r7, t12\n"
    "    mov.w r9, #37\n"
    ".align 2\n"
    "t12:\n"
    "   ldr pc, [pc, #4]\n"
    "   bkpt #9\n"
    "   nop\n"
    "   .long t13()\n"
    ".thumb\n"
    ".thumb_func\n"
    "t13:\n";

const char *tBranchLinkCondTest_s =
    "    nop\n"
    "    cmp r0, r1\n"
    "    it le\n"
    "    blle t1\n"
    "    it gt\n"
    "    blgt t2\n"
    "    b t3\n"
    "t1:\n"
    "    add r3, #1\n"
    "    mov r2, lr\n"
    "    bx lr\n"
    "t2:\n"
    "    add r3, #2\n"
    "    mov r2, lr\n"
    "    bx lr\n"
    "t3:\n"
    "    it le\n"
    "    blxle armTargetR12\n"
    "    it gt\n"
    "    blxgt armTargetR12\n"
    "    b t6\n"
    ".align 2\n"
    ".code 32\n"
    "armTargetR12:\n"
    "    eor r12, r12, lr\n"
    "    bx lr\n"
    ".thumb\n"
    "t6:\n";

const char *tBranchRegisterCondTest_s =
    "   mov r9, #0\n"
    "   mov r10, #0x60\n"
    "   mov r11, #0x1fff\n"
    "   mov r12, #0x1553\n"
    "   b.w beginTest\n"
    ".align 2\n"
    ".thumb_func\n"
    "thumbTargetR11:\n"
    "   adr r3, #-4\n"
    "   eor r3, #1\n"
    "   sub r2, lr, #8\n"
    "   eor r11, r11, r0\n"
    "   push {lr}\n"
    "   pop {pc}\n"
    ".align 2\n"
    ".code 32\n"
    "armTargetR12:\n"
    "   push {lr}\n"
    "   blx thumbTargetR11\n"
    "   eor r12, r12, r0\n"
    "   pop {pc}\n"
    ".align 2\n"
    ".code 32\n"
    "armTargetR10:\n"
    "   push {lr}\n"
    "   blx r6\n"
    "   eor r10, r10, r0\n"
    "   pop {pc}\n"
    "addrTable:\n"
    "   .long thumbTargetR11()\n"
    "   .long armTargetR12()\n"
    ".align 2\n"
    ".thumb\n"
    "beginTest:\n"
    "   mov r0, #1\n"
    "   blx armTargetR12\n"
    "   mov r0, #2\n"
    "   blx r2\n"
    "   mov r0, #4\n"
    "   blx r3\n"
    "   adr r4, addrTable\n"
    "   mov r0, #8\n"
    "   ldr r5, [r4]\n"
    "   blx r5\n"
    "   mov r0, #16\n"
    "   ldr r5, [r4, #4]\n"
    "   blx r5\n"
    "   mov r0, #32\n"
    "   ldr r6, [r4]\n"
    "   blx armTargetR10\n"
    "   mov r0, #64\n"
    "   ldr r6, [r4, #4]\n"
    "   blx armTargetR10\n"
    "   mov r0, #128\n"
    "   ldr lr, [r4]\n"
    "   blx lr\n"
    "   mov r0, #256\n"
    "   ldr lr, [r4, #4]\n"
    "   blx lr\n"
    "   mov r0, #512\n"
    "   ldr r5, [r4]\n"
    "   adr lr, t1\n"
    "   eor lr, #1\n"
    "   bx r5\n"
    ".align 2\n"
    "t1:\n"
    "   mov r0, #1024\n"
    "   ldr r5, [r4, #4]\n"
    "   adr lr, t2\n"
    "   eor lr, #1\n"
    "   bx r5\n"
    ".align 2\n"
    "t2:\n"
    "   mov r0, #2048\n"
    "   ldr r5, [r4]\n"
    "   adr lr, t3\n"
    "   eors lr, #1\n"
    "   it ge\n"
    "   bxge r5\n"
    "   eor r9, #1\n"
    "   it lt\n"
    "   bxlt r5\n"
    ".align 2\n"
    "t3:\n"
    "   mov r0, #4096\n"
    "   ldr r5, [r4, #4]\n"
    "   adr lr, t4\n"
    "   eors lr, #1\n"
    "   it ge\n"
    "   bxge r5\n"
    "   eor r9, #2\n"
    "   it lt\n"
    "   bxlt r5\n"
    ".align 2\n"
    "t4:\n"
    "   bx pc\n"
    "   bkpt #2\n"
    ".code 32\n"
    "   eors r9, r9, #4\n"
    "   blx t5\n"
    "   bkpt #3\n"
    ".thumb\n"
    ".thumb_func\n"
    "t5:\n"
    "   nop\n"
    "   it le\n"
    "   bxle pc\n"
    "   b t6\n"
    ".code 32\n"
    "   eors r9, r9, #8\n"
    "   blx t7\n"
    "   bkpt #5\n"
    ".thumb\n"
    ".thumb_func\n"
    "t6:\n"
    "   nop\n"
    "   it gt\n"
    "   bxgt pc\n"
    "   bkpt #6\n"
    ".code 32\n"
    "   eors r9, r9, #16\n"
    "   blx t7\n"
    "   bkpt #7\n"
    ".align 2\n"
    ".thumb\n"
    ".thumb_func\n"
    "t7:\n";

const char *tPushPopTest_s =
    // part 1 : tLDMIA, tSTMIA
    "p1:\n"
    "   sub sp, sp, #28\n"
    "   mov r0, sp\n"
    "   stm r0!, {r1-r7}\n"
    "   b p2\n"
    "p1_end:\n"
    "   mov r0, sp\n"
    "   ldm r0!, {r1-r7}\n"
    "   mov sp, r0\n"
    "   b end\n"
    // part 2 : tLDMIA, tSTMIA
    "p2:\n"
    "   sub sp, sp, #32\n"
    "   cmp r1, r2\n"
    "   ittee lt\n"
    "   movlt r5, sp\n"
    "   stmlt r5!, {r0-r4,r6,r7}\n"
    "   movge r0, sp\n"
    "   stmge r0!, {r1-r7}\n"
    "   b p3\n"
    ".align 2\n"
    "p2_end:\n"
    "   cmp r1, r2\n"
    "   ittee lt\n"
    "   movlt r5, sp\n"
    "   ldmlt r5!, {r0-r4,r6,r7}\n"
    "   movge r0, sp\n"
    "   ldmge r0!, {r1-r7}\n"
    "   add sp, sp, #28\n"
    "   str r5, [sp], #4\n"
    "   b p1_end\n"
    // part 3 : tPUSH, tPOP
    "p3:\n"
    "   adr lr, p2_end\n"
    "   eor lr, #1\n"
    "   push {r0-r7, lr}\n"
    "   b p4\n"
    ".align 2\n"
    "p3_end:\n"
    "   pop {r0-r7, pc}\n"
    // part 4 : tPUSH, tPOP
    "p4:\n"
    "   adr lr, p3_end\n"
    "   eor lr, #1\n"
    "   cmp r1, r2\n"
    "   ite lt\n"
    "   pushlt {r1-r7, lr}\n"
    "   pushge {r0-r6, lr}\n"
    "   b p5\n"
    "p4_end:\n"
    "   cmp r3, r4\n"
    "   ite lt\n"
    "   poplt {r0-r7}\n"
    "   popge {r0,r2-r7, pc}\n"
    "   str r7, [sp, #-8]\n"
    "   bx r7\n"
    // part 5: tLDMIA, tSTMIA no wback
    "p5:\n"
    "   cmp r11, r12\n"
    "   sub r0, sp, #32\n"
    "   ite le\n"
    "   stmle r0, {r0-r7}\n"
    "   stmgt r0, {r0,r3-r5}\n"
    "   cmp r9, r10\n"
    "   sub r2, r0, #32\n"
    "   ite le\n"
    "   stmle r2, {r0-r7}\n"
    "   stmgt r2, {r0-r2,r5,r7}\n"
    "p5_end:\n"
    "   sub r7, sp, #64\n"
    "   cmp r8, r12\n"
    "   ite le\n"
    "   ldmle r7, {r1,r3,r7}\n"
    "   ldmgt r7, {r1,r4-r7}\n"
    "   cmp r8, r11\n"
    "   ite le\n"
    "   ldmle r1, {r0,r4}\n"
    "   ldmgt r1, {r0-r5}\n"
    "   str r7, [sp, #-8]\n"
    "   str r4, [sp, #-12]\n"
    "   b p4_end\n"
    "end:\n";

const char *tLdmiaStmdbWbackTest_s =
    // part 1 : PUSH / POP
    "p1:\n"
    "   adr lr, end\n"
    "   eor lr, #1\n"
    "   push.w {r0-r12, lr}\n"
    "   b p2\n"
    "p1_end:\n"
    "   pop.w {r0-r12, pc}\n"
    // part 2 : PUSH / POP
    "p2:\n"
    "   sub sp, sp, #4\n"
    "   mov lr, sp\n"
    "   push.w {r0-r12, lr}\n"
    "   b p3\n"
    ".align 2\n"
    "p2_end:\n"
    "   pop.w {r0-r12, lr}\n"
    "   str lr, [sp], #4\n"
    "   b p1_end\n"
    // part 3 : PUSH / POP
    "   b p3\n"
    "p3:\n"
    "   adr lr, p2_end\n"
    "   eor lr, #1\n"
    "   cmp r11, r12\n"
    "   itee gt\n"
    "   pushgt.w {r0-r12, lr}\n"
    "   pushle.w {r0-r8,r10-r12, lr}\n"
    "   pushle.w {r9}\n"
    "   sub sp, sp, #4\n"
    "   b p4\n"
    "p3_end:\n"
    "   add sp, sp, #4\n"
    "   cmp r9, r12\n"
    "   ite gt\n"
    "   popgt.w {r0-r12, lr}\n"
    "   pople.w {r0-r12, pc}\n"
    "   str lr, [sp, #-56]\n"
    "   bx lr\n"
    // part 4 : LDMIA STMDB
    "p4:\n"
    "   adr lr, p4_ending\n"
    "   eor lr, #1\n"
    "   sub r9, sp, #4\n"
    "   stmdb r9!, {r0-r8,r10-r12, lr}\n"
    "   mov sp, r9\n"
    "   b p5\n"
    "p4_end:\n"
    "   mov r2, sp\n"
    "   ldmia r2!, {r0,r1,r3-r12, pc}\n"
    "   bkpt #10\n"
    ".align 2\n"
    "p4_ending:\n"
    "   mov sp, r2\n"
    "   str r9, [sp], #4\n"
    "   b p3_end\n"
    // part 5 : LDMIA STMDB
    "p5:\n"
    "   sub r2, sp, #8\n"
    "   str r2, [r2]\n"
    "   stmdb r2!, {r0,r3-r12}\n"
    "   mov sp, r2\n"
    "   b p6\n"
    ".align 2\n"
    "p5_end:\n"
    "   mov r3, sp\n"
    "   ldmia r3!, {r0-r1,r4-r12, lr}\n"
    "   mov sp, r3\n"
    "   str r9, [sp], #4\n"
    "   b p4_end\n"
    // part 6 : LDMIA STMDB
    "p6:\n"
    "   adr lr, p5_end\n"
    "   eor lr, #1\n"
    "   sub r2, sp, #4\n"
    "   cmp r11, r12\n"
    "   ite gt\n"
    "   stmdbgt r2!, {r0,r3-r12, lr}\n"
    "   stmdble r2!, {r0-r1,r3-r11, lr}\n"
    "   mov sp, r2\n"
    "p6_end:\n"
    "   mov r5, sp\n"
    "   cmp r10, r12\n"
    "   itet gt\n"
    "   addgt sp, sp, #52\n"
    "   ldmiale r5!, {r0-r4,r6-r12}\n"
    "   ldmiagt r5!, {r0-r3,r6-r12, pc}\n"
    "   mov sp, r5\n"
    "   str r12, [sp], #4\n"
    "   blx r12\n"
    ".align 2\n"
    "end:\n";

const char *tLdmdbStmiaWbackTest_s =
    // part 1 : LDMDB STMIA
    "p1:\n"
    "   adr lr, p1_ending\n"
    "   eor lr, #1\n"
    "   sub sp, sp, #56\n"
    "   mov r9, sp\n"
    "   stmia r9!, {r0-r8,r10-r12, lr}\n"
    "   b p2\n"
    "p1_end:\n"
    "   add sp, sp, #52\n"
    "   mov r2, sp\n"
    "   ldmdb r2!, {r0-r1,r3-r12, pc}\n"
    "   bkpt #10\n"
    ".align 2\n"
    "p1_ending:\n"
    "   str r2, [sp], #4\n"
    "   b end\n"
    // part 2 : LDMDB STMIA
    "p2:\n"
    "   sub sp, sp, #56\n"
    "   mov r2, sp\n"
    "   str r2, [sp, #44]\n"
    "   stmia r2!, {r0,r3-r12}\n"
    "   b p3\n"
    ".align 2\n"
    "p2_end:\n"
    "   add sp, sp, #48\n"
    "   mov r3, sp\n"
    "   ldmdb r3!, {r0,r1,r4-r12, lr}\n"
    "   str r3, [sp], #4\n"
    "   str r1, [sp], #4\n"
    "   b p1_end\n"
    // part 3 : LDMDB STMIA
    "p3:\n"
    "   adr lr, p2_end\n"
    "   eor lr, #1\n"
    "   sub sp, sp, #52\n"
    "   mov r2, sp\n"
    "   cmp r11, r12\n"
    "   ite gt\n"
    "   stmiagt r2!, {r0,r3-r12, lr}\n"
    "   stmiale r2!, {r0-r1,r3-r11, lr}\n"
    "   str r2, [sp, #-4]\n"
    "p3_end:\n"
    "   add r5, sp, #48\n"
    "   cmp r10, r12\n"
    "   itet gt\n"
    "   addgt sp, sp, #52\n"
    "   ldmdble r5!, {r0-r4,r6-r12}\n"
    "   ldmdbgt r5!, {r0-r3,r6-r12, pc}\n"
    "   add r5, r5, #48\n"
    "   mov sp, r5\n"
    "   str r12, [sp], #4\n"
    "   blx r12\n"
    ".align 2\n"
    "end:\n";

const char *tLdmiaStmdbTest_s =
    // part 1 : LDMIA STMDB
    "p1:\n"
    "   adr lr, p1_ending\n"
    "   eor lr, #1\n"
    "   sub r9, sp, #4\n"
    "   stmdb r9, {r0-r12, lr}\n"
    "   sub sp, sp, #60\n"
    "   b p2\n"
    "p1_end:\n"
    "   mov r2, sp\n"
    "   ldmia r2, {r0-r12, pc}\n"
    "   bkpt #10\n"
    ".align 2\n"
    "p1_ending:\n"
    "   add sp, sp, #56\n"
    "   str r9, [sp], #4\n"
    "   b end\n"
    // part 2 : LDMIA STMDB
    "p2:\n"
    "   sub r2, sp, #8\n"
    "   str r2, [sp, #-8]\n"
    "   stmdb r2, {r0,r2-r12}\n"
    "   sub sp, sp, #60\n"
    "   b p3\n"
    ".align 2\n"
    "p2_end:\n"
    "   mov r3, sp\n"
    "   ldmia r3, {r0-r1,r3-r12, lr}\n"
    "   str r3, [sp, #56]!\n"
    "   add sp, sp, #4\n"
    "   b p1_end\n"
    // part 3 : LDMIA STMDB
    "p3:\n"
    "   adr lr, p2_end\n"
    "   eor lr, #1\n"
    "   sub r2, sp, #4\n"
    "   cmp r11, r12\n"
    "   ite gt\n"
    "   stmdbgt r2, {r0,r2-r12, lr}\n"
    "   stmdble r2, {r0-r11, lr}\n"
    "   sub r2, r2, #52\n"
    "   mov sp, r2\n"
    "   b p4\n"
    "p3_end:\n"
    "   mov r5, sp\n"
    "   cmp r10, r12\n"
    "   itet gt\n"
    "   addgt sp, sp, #56\n"
    "   ldmiale r5, {r0-r12}\n"
    "   ldmiagt r5, {r0-r3,r5-r12, pc}\n"
    "   add sp, sp, #52\n"
    "   str r5, [sp], #4\n"
    "   blx r12\n"
    ".align 2\n"
    // part 4 : LDMIA STMDB
    "p4:\n"
    "   adr lr, p4_ending\n"
    "   eor lr, #1\n"
    "   sub r9, sp, #4\n"
    "   stmdb r9, {r0-r8,r10-r12, lr}\n"
    "   sub sp, sp, #56\n"
    "p4_end:\n"
    "   mov r2, sp\n"
    "   ldmia r2, {r0,r1,r3-r12, pc}\n"
    "   bkpt #10\n"
    ".align 2\n"
    "p4_ending:\n"
    "   add sp, sp, #52\n"
    "   str r9, [sp], #4\n"
    "   b p3_end\n"
    "end:\n";

const char *tLdmdbStmiaTest_s =
    // part 1 : LDMDB STMIA
    "p1:\n"
    "   adr lr, p1_ending\n"
    "   eor lr, #1\n"
    "   sub sp, sp, #60\n"
    "   mov r9, sp\n"
    "   stmia r9, {r0-r12,lr}\n"
    "   b p2\n"
    "p1_end:\n"
    "   add sp, sp, #56\n"
    "   mov r2, sp\n"
    "   ldmdb r2, {r0-r12,pc}\n"
    "   bkpt #10\n"
    ".align 2\n"
    "p1_ending:\n"
    "   str r2, [sp], #4\n"
    "   b end\n"
    // part 2 : LDMDB STMIA
    "p2:\n"
    "   sub sp, sp, #60\n"
    "   mov r2, sp\n"
    "   str r2, [sp, #44]\n"
    "   stmia r2, {r0,r2-r12}\n"
    "   b p3\n"
    ".align 2\n"
    "p2_end:\n"
    "   add sp, sp, #52\n"
    "   mov r3, sp\n"
    "   ldmdb r3, {r0,r1,r3-r12,lr}\n"
    "   str r3, [sp], #4\n"
    "   str r1, [sp], #4\n"
    "   b p1_end\n"
    // part 3 : LDMDB STMIA
    "p3:\n"
    "   adr lr, p2_end\n"
    "   eor lr, #1\n"
    "   sub sp, sp, #56\n"
    "   mov r2, sp\n"
    "   cmp r11, r12\n"
    "   ite gt\n"
    "   stmiagt r2, {r0,r2-r12,lr}\n"
    "   stmiale r2, {r0-r11,lr}\n"
    "   str r2, [sp, #-4]\n"
    "   b p4\n"
    "p3_end:\n"
    "   add r5, sp, #52\n"
    "   cmp r10, r12\n"
    "   itet gt\n"
    "   addgt sp, sp, #56\n"
    "   ldmdble r5, {r0-r4,r5-r12}\n"
    "   ldmdbgt r5, {r0-r3,r5-r12,pc}\n"
    "   add sp, sp, #52\n"
    "   str r5, [sp], #4\n"
    "   blx r12\n"
    ".align 2\n"
    // part 4 : LDMDB STMIA
    "p4:\n"
    "   adr lr, p4_ending\n"
    "   eor lr, #1\n"
    "   sub sp, sp, #56\n"
    "   mov r9, sp\n"
    "   stmia r9, {r0-r8,r10-r12,lr}\n"
    "p4_end:\n"
    "   add sp, sp, #52\n"
    "   mov r2, sp\n"
    "   ldmdb r2, {r0,r1,r3-r12,pc}\n"
    "   bkpt #10\n"
    ".align 2\n"
    "p4_ending:\n"
    "   str r2, [sp], #4\n"
    "   b p3_end\n"
    "end:\n";

const char *tLdrPCTest_s =
    "   mov r12, sp\n"
    // test t2LDRpci positive offset
    ".align 2\n"
    "   ldr.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldr.w r1, table1_1\n"
    // test t2LDRpci positive offset with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrle.w r2, table1_1\n"
    "   ldrgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrle.w r3, table1_1\n"
    "   ldrgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    // test t2LDRpci with PC
    "   mov r0, #0\n"
    ".align 2\n"
    "   ldr.w pc, table1_label1\n"
    "   mov r0, #1\n"
    ".thumb_func\n"
    "label1:\n"
    "   mov r1, #0\n"
    ".align 2\n"
    "   nop\n"
    "   ldr.w pc, table1_label2\n"
    "   mov r1, #1\n"
    ".thumb_func\n"
    "label2:\n"
    // test t2LDRpci with PC and cond
    "   mov r2, #0\n"
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   movle r2, #1\n"
    "   ldrgt.w pc, table1_label3\n"
    "   mov r2, #2\n"
    ".thumb_func\n"
    "label3:\n"
    "   mov r3, #0\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   movle r3, #1\n"
    "   ldrgt.w pc, table1_label4\n"
    "   mov r3, #2\n"
    ".thumb_func\n"
    "label4:\n"
    "   push {r0-r3}\n"
    // test t2LDRi12 with PC
    "   mov r0, #0\n"
    ".align 2\n"
    "   adr r4, table1_label5\n"
    "   ldr.w pc, [r4]\n"
    "   mov r0, #1\n"
    ".thumb_func\n"
    "label5:\n"
    "   mov r1, #0\n"
    ".align 2\n"
    "   nop\n"
    "   adr r4, table1_label6\n"
    "   ldr.w pc, [r4]\n"
    "   mov r1, #1\n"
    ".thumb_func\n"
    "label6:\n"
    // test t2LDRi12 with PC and cond
    "   mov r2, #0\n"
    ".align 2\n"
    "   cmp r10, r11\n"
    "   itee le\n"
    "   movle r3, #1\n"
    "   adrgt r4, table1_label7\n"
    "   ldrgt.w pc, [r4]\n"
    "   mov r2, #2\n"
    ".thumb_func\n"
    "label7:\n"
    "   mov r3, #0\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   itee le\n"
    "   movle r3, #1\n"
    "   adrgt r4, table1_label8\n"
    "   ldrgt.w pc, [r4]\n"
    "   mov r3, #2\n"
    ".thumb_func\n"
    "label8:\n"
    "   push {r0-r3}\n"
    // test tLDRpci
    ".align 2\n"
    "   ldr r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldr r1, table1_1\n"
    // test tLDRpci with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrle r2, table1_1\n"
    "   ldrgt r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrle r3, table1_1\n"
    "   ldrgt r3, table1_2\n"
    "   push {r0-r3}\n"
    "   b test2\n"
    ".align 2\n"
    "table1_0:\n"
    "   .word 0x43424140\n"
    "table1_1:\n"
    "   .word 0x47464544\n"
    "table1_2:\n"
    "   .word 0x4b4a4948\n"
    "table1_4:\n"
    "   .word 0x4f4e4d4c\n"
    "table1_label1:\n"
    "   .word label1()\n"
    "table1_label2:\n"
    "   .word label2()\n"
    "table1_label3:\n"
    "   .word label3()\n"
    "table1_label4:\n"
    "   .word label4()\n"
    "table1_label5:\n"
    "   .word label5()\n"
    "table1_label6:\n"
    "   .word label6()\n"
    "table1_label7:\n"
    "   .word label7()\n"
    "table1_label8:\n"
    "   .word label8()\n"
    "table1_label9:\n"
    "   .word label9()\n"
    "table1_label10:\n"
    "   .word label10()\n"
    "table1_label11:\n"
    "   .word label11()\n"
    "table1_label12:\n"
    "   .word label12()\n"
    "   .word label13()\n"
    "   .word label14()\n"
    "   .word label15()\n"
    "   .word label16()\n"
    // test t2LDRpci negative offset
    ".align 2\n"
    "test2:\n"
    "   ldr.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldr.w r1, table1_1\n"
    // test t2LDRpci negative offset with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrle.w r2, table1_1\n"
    "   ldrgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrle.w r3, table1_1\n"
    "   ldrgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    "   b test3\n"
    // test with a offset < -256
    ".align 8\n"
    "   nop\n"
    ".align 8\n"
    // test t2LDRpci negative offset < -256
    "test3:\n"
    "   ldr.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldr.w r1, table1_1\n"
    // test t2LDRpci negative offset < -256 with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrle.w r2, table1_1\n"
    "   ldrgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrle.w r3, table1_1\n"
    "   ldrgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    // test t2LDRpci negative offset < -256 with PC
    "   mov r0, #0\n"
    ".align 2\n"
    "   ldr.w pc, table1_label9\n"
    "   mov r0, #1\n"
    ".thumb_func\n"
    "label9:\n"
    "   mov r1, #0\n"
    ".align 2\n"
    "   nop\n"
    "   ldr.w pc, table1_label10\n"
    "   mov r1, #1\n"
    ".thumb_func\n"
    "label10:\n"
    // test t2LDRpci negative offset < -256 with PC and cond
    "   mov r2, #0\n"
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   movle r2, #1\n"
    "   ldrgt.w pc, table1_label11\n"
    "   mov r2, #2\n"
    ".thumb_func\n"
    "label11:\n"
    "   mov r3, #0\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   movle r3, #1\n"
    "   ldrgt.w pc, table1_label12\n"
    "   mov r3, #2\n"
    ".thumb_func\n"
    "label12:\n"
    "   push {r0-r3}\n"
    // test t2LDRs with PC
    "   mov r0, #0\n"
    "   adr r4, table1_label1\n"
    "   mov r5, #12\n"
    ".align 2\n"
    "   ldr.w pc, [r4, r5, lsl #2]\n"
    "   mov r0, #1\n"
    ".thumb_func\n"
    "label13:\n"
    "   mov r1, #0\n"
    "   mov r5, #13\n"
    ".align 2\n"
    "   nop\n"
    "   ldr.w pc, [r4, r5, lsl #2]\n"
    "   mov r1, #1\n"
    ".thumb_func\n"
    "label14:\n"
    // test t2LDRs with PC and cond
    "   mov r2, #0\n"
    "   mov r5, #14\n"
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   movle r3, #1\n"
    "   ldrgt.w pc, [r4, r5, lsl #2]\n"
    "   mov r2, #2\n"
    ".thumb_func\n"
    "label15:\n"
    "   mov r3, #0\n"
    "   mov r5, #15\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   movle r3, #1\n"
    "   ldrgt.w pc, [r4, r5, lsl #2]\n"
    "   mov r3, #2\n"
    ".thumb_func\n"
    "label16:\n"
    "   push {r0-r3}\n"
    ".align 2\n"
    "end:\n"
    "   mov sp, r12\n";

const char *tLdrbPCTest_s =
    "   mov r12, sp\n"
    // test t2LDRBpci positive offset
    ".align 2\n"
    "   ldrb.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrb.w r1, table1_1\n"
    // test t2LDRBpci positive offset with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrble.w r2, table1_1\n"
    "   ldrbgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrble.w r3, table1_1\n"
    "   ldrbgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    "   b test2\n"
    ".align 2\n"
    "table1_0:\n"
    "   .word 0x43424140\n"
    "table1_1:\n"
    "   .word 0x47464544\n"
    "table1_2:\n"
    "   .word 0x4b4a4948\n"
    "table1_4:\n"
    "   .word 0x4f4e4d4c\n"
    // test t2LDRBpci negative offset
    ".align 2\n"
    "test2:\n"
    "   ldrb.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrb.w r1, table1_1\n"
    // test t2LDRBpci negative offset with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrble.w r2, table1_1\n"
    "   ldrbgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrble.w r3, table1_1\n"
    "   ldrbgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    "   b test3\n"
    // test with a offset < -256
    ".align 8\n"
    "   nop\n"
    ".align 8\n"
    // test t2LDRBpci negative offset < -256
    "test3:\n"
    "   ldrb.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrb.w r1, table1_1\n"
    // test t2LDRBpci negative offset < -256 with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrble.w r2, table1_1\n"
    "   ldrbgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrble.w r3, table1_1\n"
    "   ldrbgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    ".align 2\n"
    "end:\n"
    "   mov sp, r12\n";

const char *tLdrdPCTest_s =
    "   mov r12, sp\n"
    // test t2LDRDi8 positive offset
    ".align 2\n"
    "   ldrd r0, r4, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrd r1, r5, table1_1\n"
    // test t2LDRDi8 positive offset with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrdle r2, r6, table1_1\n"
    "   ldrdgt r2, r6, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrdle r3, r7, table1_1\n"
    "   ldrdgt r3, r7, table1_2\n"
    "   push {r0-r7}\n"
    "   b test2\n"
    ".align 2\n"
    "table1_0:\n"
    "   .word 0x43424140\n"
    "table1_1:\n"
    "   .word 0x47464544\n"
    "table1_2:\n"
    "   .word 0x4b4a4948\n"
    "table1_4:\n"
    "   .word 0x4f4e4d4c\n"
    // test t2LDRDi8 negative offset
    ".align 2\n"
    "test2:\n"
    "   ldrd r0, r2, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrd r1, r3, table1_1\n"
    // test t2LDRDi8 negative offset with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrdle r4, r6, table1_1\n"
    "   ldrdgt r4, r6, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrdle r5, r7, table1_1\n"
    "   ldrdgt r5, r7, table1_2\n"
    "   push {r0-r7}\n"
    ".align 2\n"
    "end:\n"
    "   mov sp, r12\n";

const char *tLdrhPCTest_s =
    "   mov r12, sp\n"
    // test t2LDRHpci positive offset
    ".align 2\n"
    "   ldrh.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrh.w r1, table1_1\n"
    // test t2LDRHpci positive offset with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrhle.w r2, table1_1\n"
    "   ldrhgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrhle.w r3, table1_1\n"
    "   ldrhgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    "   b test2\n"
    ".align 2\n"
    "table1_0:\n"
    "   .word 0x43424140\n"
    "table1_1:\n"
    "   .word 0x47464544\n"
    "table1_2:\n"
    "   .word 0x4b4a4948\n"
    "table1_4:\n"
    "   .word 0x4f4e4d4c\n"
    // test t2LDRHpci negative offset
    ".align 2\n"
    "test2:\n"
    "   ldrh.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrh.w r1, table1_1\n"
    // test t2LDRHpci negative offset with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrhle.w r2, table1_1\n"
    "   ldrhgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrhle.w r3, table1_1\n"
    "   ldrhgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    "   b test3\n"
    // test with a offset < -256
    ".align 8\n"
    "   nop\n"
    ".align 8\n"
    // test t2LDRHpci negative offset < -256
    "test3:\n"
    "   ldrh.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrh.w r1, table1_1\n"
    // test t2LDRHpci negative offset < -256 with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrhle.w r2, table1_1\n"
    "   ldrhgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrhle.w r3, table1_1\n"
    "   ldrhgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    ".align 2\n"
    "end:\n"
    "   mov sp, r12\n";

const char *tLdrsbPCTest_s =
    "   mov r12, sp\n"
    // test t2LDRSBpci positive offset
    ".align 2\n"
    "   ldrsb.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrsb.w r1, table1_1\n"
    // test t2LDRSBpci positive offset with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrsble.w r2, table1_1\n"
    "   ldrsbgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrsble.w r3, table1_1\n"
    "   ldrsbgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    "   b test2\n"
    ".align 2\n"
    "table1_0:\n"
    "   .word 0x83828180\n"
    "table1_1:\n"
    "   .word 0x87868584\n"
    "table1_2:\n"
    "   .word 0x8b8a8988\n"
    "table1_4:\n"
    "   .word 0x8f8e8d8c\n"
    // test t2LDRSBpci negative offset
    ".align 2\n"
    "test2:\n"
    "   ldrsb.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrsb.w r1, table1_1\n"
    // test t2LDRSBpci negative offset with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrsble.w r2, table1_1\n"
    "   ldrsbgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrsble.w r3, table1_1\n"
    "   ldrsbgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    "   b test3\n"
    // test with a offset < -256
    ".align 8\n"
    "   nop\n"
    ".align 8\n"
    // test t2LDRSBpci negative offset < -256
    "test3:\n"
    "   ldrsb.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrsb.w r1, table1_1\n"
    // test t2LDRSBpci negative offset < -256 with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrsble.w r2, table1_1\n"
    "   ldrsbgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrsble.w r3, table1_1\n"
    "   ldrsbgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    ".align 2\n"
    "end:\n"
    "   mov sp, r12\n";

const char *tLdrshPCTest_s =
    "   mov r12, sp\n"
    // test t2LDRSHpci positive offset
    ".align 2\n"
    "   ldrsh.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrsh.w r1, table1_1\n"
    // test t2LDRSHpci positive offset with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrshle.w r2, table1_1\n"
    "   ldrshgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrshle.w r3, table1_1\n"
    "   ldrshgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    "   b test2\n"
    ".align 2\n"
    "table1_0:\n"
    "   .word 0x83828180\n"
    "table1_1:\n"
    "   .word 0x87868584\n"
    "table1_2:\n"
    "   .word 0x8b8a8988\n"
    "table1_4:\n"
    "   .word 0x8f8e8d8c\n"
    // test t2LDRSHpci negative offset
    ".align 2\n"
    "test2:\n"
    "   ldrsh.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrsh.w r1, table1_1\n"
    // test t2LDRSHpci negative offset with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrshle.w r2, table1_1\n"
    "   ldrshgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrshle.w r3, table1_1\n"
    "   ldrshgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    "   b test3\n"
    // test with a offset < -256
    ".align 8\n"
    "   nop\n"
    ".align 8\n"
    // test t2LDRSHpci negative offset < -256
    "test3:\n"
    "   ldrsh.w r0, table1_1\n"
    ".align 2\n"
    "   nop\n"
    "   ldrsh.w r1, table1_1\n"
    // test t2LDRSHpci negative offset < -256 with cond
    ".align 2\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrshle.w r2, table1_1\n"
    "   ldrshgt.w r2, table1_2\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r10, r11\n"
    "   ite le\n"
    "   ldrshle.w r3, table1_1\n"
    "   ldrshgt.w r3, table1_2\n"
    "   push {r0-r3}\n"
    ".align 2\n"
    "end:\n"
    "   mov sp, r12\n";

const char *tMovPCTest_s =
    "   mov r12, sp\n"
    "   mov r0, #42\n"
    "   mov r1, #74\n"
    ".align 2\n"
    // tMOVr pc, pc
    "   mov pc, pc\n"
    "   b t1\n"
    "   push {r0}\n"
    "   b t2\n"
    "t1:\n"
    "   push {r1}\n"
    "t2:\n"
    ".align 2\n"
    "   nop\n"
    "   mov pc, pc\n"
    "   b t3\n"
    "   push {r0}\n"
    "   b t4\n"
    "t3:\n"
    "   push {r1}\n"
    "t4:\n"
    ".align 2\n"
    // tMOVr pc, pc with cond
    "   cmp r11, r12\n"
    "   it le\n"
    "   movle pc, pc\n"
    "   b t5\n"
    "   push {r0}\n"
    "   b t6\n"
    "t5:\n"
    "   push {r1}\n"
    "t6:\n"
    ".align 2\n"
    "   nop\n"
    "   cmp r11, r12\n"
    "   it le\n"
    "   movle pc, pc\n"
    "   b t7\n"
    "   push {r0}\n"
    "   b t8\n"
    "t7:\n"
    "   push {r1}\n"
    "t8:\n"
    ".align 2\n"
    // tMOVr pc, <reg>
    "   adr r7, t9\n"
    "   mov pc, r7\n"
    "   push {r0}\n"
    "   b t10\n"
    ".align 2\n"
    "t9:\n"
    "   push {r1}\n"
    ".align 2\n"
    "t10:\n"
    "   nop\n"
    "   adr r7, t11\n"
    "   mov pc, pc\n"
    "   push {r0}\n"
    "   b t12\n"
    ".align 2\n"
    "t11:\n"
    "   push {r1}\n"
    ".align 2\n"
    "t12:\n"
    // tMOVr pc, <reg> with cond
    "   cmp r11, r12\n"
    "   adr r7, t13\n"
    "   it le\n"
    "   movle pc, r7\n"
    "   push {r0}\n"
    "   b t14\n"
    ".align 2\n"
    "t13:\n"
    "   push {r1}\n"
    ".align 2\n"
    "t14:\n"
    "   nop\n"
    "   cmp r11, r12\n"
    "   adr r7, t15\n"
    "   it le\n"
    "   movle pc, r7\n"
    "   push {r0}\n"
    "   b t16\n"
    ".align 2\n"
    "t15:\n"
    "   push {r1}\n"
    ".align 2\n"
    "t16:\n"
    // tMOVr <reg>, pc
    "   mov r0, pc\n"
    "   mov r1, pc\n"
    "   cmp r11, r12\n"
    ".align 2\n"
    "   ittee le\n"
    "   movle r2, pc\n"
    "   movle r3, pc\n"
    "   movgt r2, pc\n"
    "   movgt r3, pc\n"
    "   push {r0-r3}\n"
    "end:\n"
    "   mov sp, r12\n";

const char *tTBBTest_s =
    "   mov r12, sp\n"
    // test 1: aligned TBB pc
    "   mov r0, r1\n"
    "   bl foot1\n"
    "   mov r0, r2\n"
    "   bl foot1\n"
    "   mov r0, r3\n"
    "   bl foot1\n"
    "   mov r0, r4\n"
    "   bl foot1\n"
    "   mov r0, r5\n"
    "   bl foot1\n"
    "   mov r0, r6\n"
    "   bl foot1\n"
    "   mov r0, r7\n"
    "   bl foot1\n"
    "   mov r0, r8\n"
    "   bl foot1\n"
    "   mov r0, r9\n"
    "   bl foot1\n"
    "   mov r0, r10\n"
    "   bl foot1\n"
    "   mov r0, r11\n"
    "   bl foot1\n"
    "   b t2\n"
    "foot1:\n"
    "   and r0, 7\n"
    ".align 4\n"
    "   tbb [pc, r0]\n"
    ".byte 6\n"
    ".byte 14\n"
    ".byte 22\n"
    ".byte 30\n"
    ".byte 38\n"
    ".byte 46\n"
    ".byte 54\n"
    ".byte 62\n"
    ".align 4\n"
    // target tbb 1
    "   mov r0, #1\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 2
    "   mov r0, #2\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 3
    "   mov r0, #3\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 4
    "   mov r0, #4\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 5
    "   mov r0, #5\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 6
    "   mov r0, #6\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 7
    "   mov r0, #7\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 8
    "   mov r0, #8\n"
    "   push {r0}\n"
    "   bx lr\n"
    "t2:\n"
    // test 2: unaligned TBB pc
    "   mov r0, r1\n"
    "   bl foot2\n"
    "   mov r0, r2\n"
    "   bl foot2\n"
    "   mov r0, r3\n"
    "   bl foot2\n"
    "   mov r0, r4\n"
    "   bl foot2\n"
    "   mov r0, r5\n"
    "   bl foot2\n"
    "   mov r0, r6\n"
    "   bl foot2\n"
    "   mov r0, r7\n"
    "   bl foot2\n"
    "   mov r0, r8\n"
    "   bl foot2\n"
    "   mov r0, r9\n"
    "   bl foot2\n"
    "   mov r0, r10\n"
    "   bl foot2\n"
    "   mov r0, r11\n"
    "   bl foot2\n"
    "   b t3\n"
    "foot2:\n"
    "   and r0, 7\n"
    ".align 4\n"
    "   nop\n"
    "   tbb [pc, r0]\n"
    ".byte 5\n"
    ".byte 13\n"
    ".byte 21\n"
    ".byte 29\n"
    ".byte 37\n"
    ".byte 45\n"
    ".byte 53\n"
    ".byte 61\n"
    ".align 4\n"
    // target tbb 1
    "   mov r0, #1\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 2
    "   mov r0, #2\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 3
    "   mov r0, #3\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 4
    "   mov r0, #4\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 5
    "   mov r0, #5\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 6
    "   mov r0, #6\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 7
    "   mov r0, #7\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 8
    "   mov r0, #8\n"
    "   push {r0}\n"
    "   bx lr\n"
    "t3:\n"
    // test 3: aligned TBB <reg>
    "   mov r0, r2\n"
    "   bl foot3\n"
    "   mov r0, r3\n"
    "   bl foot3\n"
    "   mov r0, r4\n"
    "   bl foot3\n"
    "   mov r0, r5\n"
    "   bl foot3\n"
    "   mov r0, r6\n"
    "   bl foot3\n"
    "   mov r0, r7\n"
    "   bl foot3\n"
    "   mov r0, r8\n"
    "   bl foot3\n"
    "   mov r0, r9\n"
    "   bl foot3\n"
    "   mov r0, r10\n"
    "   bl foot3\n"
    "   mov r0, r11\n"
    "   bl foot3\n"
    "   b t4\n"
    ".align 2\n"
    "tablet3:\n"
    ".byte 6\n"
    ".byte 14\n"
    ".byte 22\n"
    ".byte 30\n"
    ".byte 38\n"
    ".byte 46\n"
    ".byte 54\n"
    ".byte 62\n"
    "foot3:\n"
    "   and r0, 7\n"
    "   adr r1, tablet3\n"
    ".align 4\n"
    "   tbb [r1, r0]\n"
    ".align 4\n"
    // target tbb 1
    "   mov r0, #1\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 2
    "   mov r0, #2\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 3
    "   mov r0, #3\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 4
    "   mov r0, #4\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 5
    "   mov r0, #5\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 6
    "   mov r0, #6\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 7
    "   mov r0, #7\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 8
    "   mov r0, #8\n"
    "   push {r0}\n"
    "   bx lr\n"
    "t4:\n"
    // test 4: aligned TBB <reg>
    "   mov r0, r2\n"
    "   bl foot4\n"
    "   mov r0, r3\n"
    "   bl foot4\n"
    "   mov r0, r4\n"
    "   bl foot4\n"
    "   mov r0, r5\n"
    "   bl foot4\n"
    "   mov r0, r6\n"
    "   bl foot4\n"
    "   mov r0, r7\n"
    "   bl foot4\n"
    "   mov r0, r8\n"
    "   bl foot4\n"
    "   mov r0, r9\n"
    "   bl foot4\n"
    "   mov r0, r10\n"
    "   bl foot4\n"
    "   mov r0, r11\n"
    "   bl foot4\n"
    "   b t5\n"
    ".align 2\n"
    "tablet4:\n"
    ".byte 5\n"
    ".byte 13\n"
    ".byte 21\n"
    ".byte 29\n"
    ".byte 37\n"
    ".byte 45\n"
    ".byte 53\n"
    ".byte 61\n"
    "foot4:\n"
    "   and r0, 7\n"
    "   adr r1, tablet4\n"
    ".align 4\n"
    "   nop\n"
    "   tbb [r1, r0]\n"
    ".align 4\n"
    // target tbb 1
    "   mov r0, #1\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 2
    "   mov r0, #2\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 3
    "   mov r0, #3\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 4
    "   mov r0, #4\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 5
    "   mov r0, #5\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 6
    "   mov r0, #6\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 7
    "   mov r0, #7\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 8
    "   mov r0, #8\n"
    "   push {r0}\n"
    "   bx lr\n"
    "t5:\n"
    // test 5: TBB <reg> cond
    "   mov r0, r2\n"
    "   bl foot5\n"
    "   mov r0, r3\n"
    "   bl foot5\n"
    "   mov r0, r4\n"
    "   bl foot5\n"
    "   mov r0, r5\n"
    "   bl foot5\n"
    "   mov r0, r6\n"
    "   bl foot5\n"
    "   mov r0, r7\n"
    "   bl foot5\n"
    "   mov r0, r8\n"
    "   bl foot5\n"
    "   mov r0, r9\n"
    "   bl foot5\n"
    "   mov r0, r10\n"
    "   bl foot5\n"
    "   mov r0, r11\n"
    "   bl foot5\n"
    "   b end\n"
    ".align 2\n"
    "tablet5_1:\n"
    ".byte 3\n"
    ".byte 11\n"
    ".byte 19\n"
    ".byte 27\n"
    ".byte 35\n"
    ".byte 43\n"
    ".byte 51\n"
    ".byte 59\n"
    ".align 2\n"
    "tablet5_2:\n"
    ".byte 25\n"
    ".byte 57\n"
    ".byte 33\n"
    ".byte 9\n"
    ".byte 41\n"
    ".byte 1\n"
    ".byte 17\n"
    ".byte 49\n"
    "foot5:\n"
    "   and r0, 7\n"
    "   adr r1, tablet5_1\n"
    "   cmp r10, r11\n"
    ".align 4\n"
    "   ite ge\n"
    "   adrge.w r1, tablet5_2\n"
    "   tbblt [r1, r0]\n"
    "   tbb [r1, r0]\n"
    ".align 4\n"
    // target tbb 1
    "   mov r0, #1\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 2
    "   mov r0, #2\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 3
    "   mov r0, #3\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 4
    "   mov r0, #4\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 5
    "   mov r0, #5\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 6
    "   mov r0, #6\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 7
    "   mov r0, #7\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbb 8
    "   mov r0, #8\n"
    "   push {r0}\n"
    "   bx lr\n"
    "end:\n"
    "   mov sp, r12\n";

const char *tTBHTest_s =
    "   mov r12, sp\n"
    // test 1: aligned TBH pc
    "   mov r0, r1\n"
    "   bl foot1\n"
    "   mov r0, r2\n"
    "   bl foot1\n"
    "   mov r0, r3\n"
    "   bl foot1\n"
    "   mov r0, r4\n"
    "   bl foot1\n"
    "   mov r0, r5\n"
    "   bl foot1\n"
    "   mov r0, r6\n"
    "   bl foot1\n"
    "   mov r0, r7\n"
    "   bl foot1\n"
    "   mov r0, r8\n"
    "   bl foot1\n"
    "   mov r0, r9\n"
    "   bl foot1\n"
    "   mov r0, r10\n"
    "   bl foot1\n"
    "   mov r0, r11\n"
    "   bl foot1\n"
    "   b t2\n"
    "foot1:\n"
    "   and r0, 7\n"
    ".align 4\n"
    "   tbh [pc, r0, lsl #1]\n"
    ".hword 14\n"
    ".hword 22\n"
    ".hword 30\n"
    ".hword 38\n"
    ".hword 46\n"
    ".hword 54\n"
    ".hword 62\n"
    ".hword 70\n"
    ".align 4\n"
    // target tbh 1
    "   mov r0, #1\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 2
    "   mov r0, #2\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 3
    "   mov r0, #3\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 4
    "   mov r0, #4\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 5
    "   mov r0, #5\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 6
    "   mov r0, #6\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 7
    "   mov r0, #7\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 8
    "   mov r0, #8\n"
    "   push {r0}\n"
    "   bx lr\n"
    "t2:\n"
    // test 2: unaligned TBH pc
    "   mov r0, r1\n"
    "   bl foot2\n"
    "   mov r0, r2\n"
    "   bl foot2\n"
    "   mov r0, r3\n"
    "   bl foot2\n"
    "   mov r0, r4\n"
    "   bl foot2\n"
    "   mov r0, r5\n"
    "   bl foot2\n"
    "   mov r0, r6\n"
    "   bl foot2\n"
    "   mov r0, r7\n"
    "   bl foot2\n"
    "   mov r0, r8\n"
    "   bl foot2\n"
    "   mov r0, r9\n"
    "   bl foot2\n"
    "   mov r0, r10\n"
    "   bl foot2\n"
    "   mov r0, r11\n"
    "   bl foot2\n"
    "   b t3\n"
    "foot2:\n"
    "   and r0, 7\n"
    ".align 4\n"
    "   nop\n"
    "   tbh [pc, r0, lsl #1]\n"
    ".hword 13\n"
    ".hword 21\n"
    ".hword 29\n"
    ".hword 37\n"
    ".hword 45\n"
    ".hword 53\n"
    ".hword 61\n"
    ".hword 69\n"
    ".align 4\n"
    // target tbh 1
    "   mov r0, #1\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 2
    "   mov r0, #2\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 3
    "   mov r0, #3\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 4
    "   mov r0, #4\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 5
    "   mov r0, #5\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 6
    "   mov r0, #6\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 7
    "   mov r0, #7\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 8
    "   mov r0, #8\n"
    "   push {r0}\n"
    "   bx lr\n"
    "t3:\n"
    // test 3: aligned TBH <reg>
    "   mov r0, r2\n"
    "   bl foot3\n"
    "   mov r0, r3\n"
    "   bl foot3\n"
    "   mov r0, r4\n"
    "   bl foot3\n"
    "   mov r0, r5\n"
    "   bl foot3\n"
    "   mov r0, r6\n"
    "   bl foot3\n"
    "   mov r0, r7\n"
    "   bl foot3\n"
    "   mov r0, r8\n"
    "   bl foot3\n"
    "   mov r0, r9\n"
    "   bl foot3\n"
    "   mov r0, r10\n"
    "   bl foot3\n"
    "   mov r0, r11\n"
    "   bl foot3\n"
    "   b t4\n"
    ".align 2\n"
    "tablet3:\n"
    ".hword 6\n"
    ".hword 14\n"
    ".hword 22\n"
    ".hword 30\n"
    ".hword 38\n"
    ".hword 46\n"
    ".hword 54\n"
    ".hword 62\n"
    "foot3:\n"
    "   and r0, 7\n"
    "   adr r1, tablet3\n"
    ".align 4\n"
    "   tbh [r1, r0, lsl #1]\n"
    ".align 4\n"
    // target tbh 1
    "   mov r0, #1\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 2
    "   mov r0, #2\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 3
    "   mov r0, #3\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 4
    "   mov r0, #4\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 5
    "   mov r0, #5\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 6
    "   mov r0, #6\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 7
    "   mov r0, #7\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 8
    "   mov r0, #8\n"
    "   push {r0}\n"
    "   bx lr\n"
    "t4:\n"
    // test 4: aligned TBH <reg>
    "   mov r0, r2\n"
    "   bl foot4\n"
    "   mov r0, r3\n"
    "   bl foot4\n"
    "   mov r0, r4\n"
    "   bl foot4\n"
    "   mov r0, r5\n"
    "   bl foot4\n"
    "   mov r0, r6\n"
    "   bl foot4\n"
    "   mov r0, r7\n"
    "   bl foot4\n"
    "   mov r0, r8\n"
    "   bl foot4\n"
    "   mov r0, r9\n"
    "   bl foot4\n"
    "   mov r0, r10\n"
    "   bl foot4\n"
    "   mov r0, r11\n"
    "   bl foot4\n"
    "   b t5\n"
    ".align 2\n"
    "tablet4:\n"
    ".hword 5\n"
    ".hword 13\n"
    ".hword 21\n"
    ".hword 29\n"
    ".hword 37\n"
    ".hword 45\n"
    ".hword 53\n"
    ".hword 61\n"
    "foot4:\n"
    "   and r0, 7\n"
    "   adr r1, tablet4\n"
    ".align 4\n"
    "   nop\n"
    "   tbh [r1, r0, lsl #1]\n"
    ".align 4\n"
    // target tbh 1
    "   mov r0, #1\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 2
    "   mov r0, #2\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 3
    "   mov r0, #3\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 4
    "   mov r0, #4\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 5
    "   mov r0, #5\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 6
    "   mov r0, #6\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 7
    "   mov r0, #7\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 8
    "   mov r0, #8\n"
    "   push {r0}\n"
    "   bx lr\n"
    "t5:\n"
    // test 5: TBH <reg> cond
    "   mov r0, r2\n"
    "   bl foot5\n"
    "   mov r0, r3\n"
    "   bl foot5\n"
    "   mov r0, r4\n"
    "   bl foot5\n"
    "   mov r0, r5\n"
    "   bl foot5\n"
    "   mov r0, r6\n"
    "   bl foot5\n"
    "   mov r0, r7\n"
    "   bl foot5\n"
    "   mov r0, r8\n"
    "   bl foot5\n"
    "   mov r0, r9\n"
    "   bl foot5\n"
    "   mov r0, r10\n"
    "   bl foot5\n"
    "   mov r0, r11\n"
    "   bl foot5\n"
    "   b end\n"
    ".align 2\n"
    "tablet5_1:\n"
    ".hword 3\n"
    ".hword 11\n"
    ".hword 19\n"
    ".hword 27\n"
    ".hword 35\n"
    ".hword 43\n"
    ".hword 51\n"
    ".hword 59\n"
    ".align 2\n"
    "tablet5_2:\n"
    ".hword 25\n"
    ".hword 57\n"
    ".hword 33\n"
    ".hword 9\n"
    ".hword 41\n"
    ".hword 1\n"
    ".hword 17\n"
    ".hword 49\n"
    "foot5:\n"
    "   and r0, 7\n"
    "   adr r1, tablet5_1\n"
    "   cmp r10, r11\n"
    ".align 4\n"
    "   ite ge\n"
    "   adrge.w r1, tablet5_2\n"
    "   tbhlt [r1, r0, lsl #1]\n"
    "   tbh [r1, r0, lsl #1]\n"
    ".align 4\n"
    // target tbh 1
    "   mov r0, #1\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 2
    "   mov r0, #2\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 3
    "   mov r0, #3\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 4
    "   mov r0, #4\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 5
    "   mov r0, #5\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 6
    "   mov r0, #6\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 7
    "   mov r0, #7\n"
    "   push {r0}\n"
    "   bx lr\n"
    ".align 4\n"
    // target tbh 8
    "   mov r0, #8\n"
    "   push {r0}\n"
    "   bx lr\n"
    "end:\n"
    "   mov sp, r12\n";

const char *tITCondTest_s =
    "   mov r12, sp\n"
    "   b test_start\n"
    ".align 4\n"
    ".thumb_func\n"
    "push_apsr:\n"
    "   mrs r0, APSR\n"
    "   push {r0}\n"
    "   bx lr\n"
    "test_start:\n"
    "   cmp r11, r12\n"
    "   bl push_apsr\n"
    "   adds r1, #12\n"
    "   it al\n"
    "   addal r2, #12\n"
    "   bl push_apsr\n"
    // test change flags in ITBlock
    "   mov r0, #0\n"
    "   cmp r10, r11\n"
    "   ittee gt\n"
    "   cmpgt r11, r10\n"
    "   movgt r4, r0\n"
    "   cmple r11, r10\n"
    "   movle r5, r0\n"
    "   push {r4, r5}\n"
    "   bl push_apsr\n"
    "end:\n"
    "   mov sp, r12\n";

const char *tldrexTest_s =
    "   mov r12, sp\n"
    "   mov r2, #0\n"
    "loopmemset:\n"
    "   strh r2, [r11, r2, lsl #1]\n"
    "   add r2, #1\n"
    "   cmp r2, 2048\n"
    "   bne loopmemset\n"
    // align 4 r11 (to support ldrexd)
    "   orr r11, r11, 0xf\n"
    "   add r11, r11, 1\n"
    // test 1
    "   ldrex r0, [r11]\n"
    "   mov r1, #0xff\n"
    "   strex r2, r1, [r11]\n"
    "   ldr r1, [r11]\n"
    "   push {r0, r1, r2}\n"
    // test 2
    "   ldrex r0, [r11, #256]\n"
    "   mov r1, #0xfa7\n"
    "   add r10, r11, #256\n"
    "   strex r2, r1, [r10]\n"
    "   ldr r1, [r11, #256]\n"
    "   push {r0, r1, r2}\n"
    // test 3
    "   ldrex r0, [r11]\n"
    "   mov r1, #0xfa8\n"
    "   strex r2, r1, [r11, #300]\n"
    "   ldr r1, [r11, #300]\n"
    "   push {r0, r1, r2}\n"
    // test 4
    "   add r10, r11, #2048\n"
    "   ldrex r0, [r11]\n"
    "   ldrex r1, [r10]\n"
    "   mov r2, #0xfa8\n"
    "   mov r3, #0xc58\n"
    "   strex r4, r2, [r11]\n"
    "   strex r5, r3, [r10]\n"
    "   ldr r6, [r11]\n"
    "   ldr r7, [r10]\n"
    "   push {r0-r7}\n"
    // test 5
    "   add r10, r11, #3096\n"
    "   ldrex r0, [r11]\n"
    "   ldrex r1, [r10]\n"
    "   mov r2, #0x1a4\n"
    "   mov r3, #0x453\n"
    "   strex r4, r2, [r10]\n"
    "   strex r5, r3, [r11]\n"
    "   ldr r6, [r11]\n"
    "   ldr r7, [r10]\n"
    "   push {r0-r7}\n"
    // test 6
    "   mov r0, #0\n"
    "   mov r1, #0\n"
    "   add r10, r11, #3096\n"
    "   cmp r8, r9\n"
    "   ite le\n"
    "   ldrexle r0, [r11]\n"
    "   ldrexgt r1, [r10]\n"
    "   mov r2, #0x58\n"
    "   mov r3, #0x761\n"
    "   mov r4, #2\n"
    "   mov r5, #2\n"
    "   ite le\n"
    "   strexle r5, r3, [r11]\n"
    "   strexgt r4, r2, [r10]\n"
    "   ldr r6, [r11]\n"
    "   ldr r7, [r10]\n"
    "   push {r0-r7}\n"
    // test 7
    "   mov r0, #0\n"
    "   mov r1, #0\n"
    "   add r10, r11, #3080\n"
    "   cmp r8, r9\n"
    "   ite le\n"
    "   ldrexle r0, [r11]\n"
    "   ldrexgt r1, [r10]\n"
    "   mov r2, #0x146\n"
    "   mov r3, #0x9de\n"
    "   mov r4, #2\n"
    "   mov r5, #2\n"
    "   ite le\n"
    "   strexle r4, r2, [r10]\n"
    "   strexgt r5, r3, [r11]\n"
    "   ldr r6, [r11]\n"
    "   ldr r7, [r10]\n"
    "   push {r0-r7}\n"
    // test 8
    "   mov r0, #0\n"
    "   mov r1, #0\n"
    "   add r10, r11, #3000\n"
    "   cmp r9, r8\n"
    "   ite le\n"
    "   ldrexhle r0, [r11]\n"
    "   ldrexbgt r1, [r10]\n"
    "   mov r2, #0xb5\n"
    "   mov r3, #0xea\n"
    "   mov r4, #2\n"
    "   mov r5, #2\n"
    "   ite le\n"
    "   strexhle r5, r3, [r11]\n"
    "   strexbgt r4, r2, [r10]\n"
    "   ldr r6, [r11]\n"
    "   ldr r7, [r10]\n"
    "   push {r0-r7}\n"
    // test 9
    "   mov r0, #0\n"
    "   mov r1, #0\n"
    "   add r10, r11, #1032\n"
    "   cmp r9, r8\n"
    "   ite le\n"
    "   ldrexdle r0, r8, [r11]\n"
    "   ldrexbgt r1, [r10]\n"
    "   mov r2, #0x78\n"
    "   mov r3, #0x46d\n"
    "   mov r4, #2\n"
    "   mov r5, #2\n"
    "   ite le\n"
    "   strexble r5, r3, [r11]\n"
    "   strexdgt r4, r9, r2, [r10]\n"
    "   ldr r6, [r11]\n"
    "   ldr r7, [r10]\n"
    "   push {r0-r7}\n"
    "end:\n"
    "   mov sp, r12\n";
